#include <string.h>
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>

#include "queue.h"
#include "method.h"
#include "log.h"
#include "msg.h"

#define MAX_EXTER_LIB_NUMBER 16
#define INNER_FUNCTION_PREFIX "inner_"

typedef int  (*rq_handler)(const queue_header* header, const void* data);
typedef int  (*rq_init)();
typedef void (*rq_fini)();

typedef struct library{
	void* library;
	rq_handler handler;
	char extra[FILE_PATH_SIZE];
}library;

static library lib[HANDLER_NUMBER];
static library ext[MAX_EXTER_LIB_NUMBER];

static int rq_exter_handler();

static int rq_db_loader();
static int rq_inner_loader();
static int rq_exter_loader();

static void rq_exter_destroy();

int rq_initialize(int index)
{
	switch(index)
	{
	case QUEUE_INDEX_HTTP:  // HTTP
		break;
	case QUEUE_INDEX_SHM:   // SHM
		break;
	case QUEUE_INDEX_DB:    // DB
		return rq_db_loader();

	case QUEUE_INDEX_INNER: // INNER
		return rq_inner_loader();

	case QUEUE_INDEX_EXTER: // EXTER
		return rq_exter_loader();
	default:
		break;
	}

	return -1;
}

int rq_handle(int index, const queue_header* header, const void* data)
{
	switch(index)
	{
	case QUEUE_INDEX_HTTP:  // HTTP
		break;
	case QUEUE_INDEX_SHM:   // SHM
		break;
	case QUEUE_INDEX_MSG:   // MSG
		break;
	case QUEUE_INDEX_DB:    // DB
		if(lib[QUEUE_INDEX_DB].library == NULL) {
			/* maybe the mysql connection lost. */
			rq_destroy(QUEUE_INDEX_DB);
			if(rq_initialize(QUEUE_INDEX_DB) == -1)
				return -1;
		}
		return (*lib[QUEUE_INDEX_DB].handler)(header, data);

	case QUEUE_INDEX_INNER: // INNER
		if(lib[QUEUE_INDEX_INNER].handler == NULL)
			return -1;
		return (*lib[QUEUE_INDEX_INNER].handler)(header, data);

	case QUEUE_INDEX_EXTER: // EXTER
		return rq_exter_handler(header, data);

	default:
		break;
	}

	return 1;
}

void rq_destroy(int index)
{
	rq_fini destroy;
	switch(index)
	{
	case QUEUE_INDEX_HTTP: // HTTP
		break;
	case QUEUE_INDEX_SHM: // SHM
		break;
	case QUEUE_INDEX_MSG: // MSG
		break;
	case QUEUE_INDEX_DB:    // DB
		if(lib[QUEUE_INDEX_DB].library != NULL)
		{
			if((destroy = dlsym(lib[QUEUE_INDEX_DB].library, "db_destroy")) != NULL)
				(*destroy)();
			dlclose(lib[QUEUE_INDEX_DB].library);
		}
		break;

	case QUEUE_INDEX_INNER: // INNER
		if(lib[QUEUE_INDEX_INNER].library != NULL)
			dlclose(lib[QUEUE_INDEX_INNER].library);
		break;

	case QUEUE_INDEX_EXTER: // EXTER
		rq_exter_destroy();
		break;
	default:
		break;
	}
	
	lib[index].library = NULL;
	lib[index].handler = NULL;
}

int rq_exter_handler(const queue_header* header, const void* data)
{
	library *extlib;
	extlib = ext;
	while(   extlib->library != NULL
	      && strcmp(header->ext, extlib->extra) != 0)
		extlib++;

	if(extlib->library == NULL) {
		logger(PRIORITY_ERROR, "'%s' handler not exist at %d in '%s'.", header->ext, __LINE__, __FILE__);
		return 0;
	}

	return (*extlib->handler)(header, data);
}

int rq_db_loader()
{
	int re;
	rq_init initialize;
	snprintf(lib[QUEUE_INDEX_DB].extra, FILE_PATH_SIZE, "%s/lib/mysql.so", queue_root);
	if((lib[QUEUE_INDEX_DB].library = dlopen(lib[QUEUE_INDEX_DB].extra,  RTLD_LOCAL|RTLD_LAZY)) == NULL)
	{
		logger(PRIORITY_ERROR, "%s at %d in '%s'.", dlerror(), __LINE__, __FILE__);
		return -1;
	}
	if((initialize = dlsym(lib[QUEUE_INDEX_DB].library, "db_initialize")) == NULL)
		goto error_load;

	if(-1 == (re = (*initialize)())) {
		logger(PRIORITY_ERROR, "initialize code(%d), in library mysql.so", re);
		goto error_load;
	}

	/* here was told that had some memory heap, but I'm not found. */

	if((lib[QUEUE_INDEX_DB].handler = dlsym(lib[QUEUE_INDEX_DB].library, "db_handler")) == NULL)
		goto error_load;
	return 1;

error_load:
	dlclose(lib[QUEUE_INDEX_DB].library);
	lib[QUEUE_INDEX_DB].library = NULL;
	lib[QUEUE_INDEX_DB].handler = NULL;
	logger(PRIORITY_ERROR, "%s at %d in '%s'.", dlerror(), __LINE__, __FILE__);
	return -1;
}

int rq_inner_loader()
{
	const char* name;
	snprintf(lib[QUEUE_INDEX_INNER].extra, FILE_PATH_SIZE, "%s/lib/inner.so", queue_root);
	if((lib[QUEUE_INDEX_INNER].library = dlopen(lib[QUEUE_INDEX_INNER].extra, RTLD_LAZY)) == NULL) {
		logger(PRIORITY_ERROR, "%s at %d in '%s'.\n", dlerror(), __LINE__, __FILE__);
		return -1;
	}

	if(NULL == (name = INI_GetValue(cfg, "default", "InnerHandler"))) {
		logger(PRIORITY_ERROR, "no 'InnerHandler' in config file.");
		return -1;
	}

	snprintf(lib[QUEUE_INDEX_INNER].extra, FILE_PATH_SIZE, "%s%s", INNER_FUNCTION_PREFIX, name);
	if(NULL == (lib[QUEUE_INDEX_INNER].handler = dlsym(lib[QUEUE_INDEX_INNER].library, lib[QUEUE_INDEX_INNER].extra)))
	{
		logger(PRIORITY_ERROR, "inner function '%s' not exist. %s", lib[QUEUE_INDEX_INNER].extra, dlerror());
		return -1;
	}
	return 1;
}

int rq_exter_loader()
{
	const char *list;
	char *tmp, *file;
	char function[64], libfile[FILE_PATH_SIZE];
	int re, index = 0;
	rq_init initialize;
	memset(ext, 0x0, sizeof(ext));
	if(NULL == (list = INI_GetValue(cfg, "default", "Extension"))){
		logger(PRIORITY_DEBUG, "no exter extension libraries defined.");
		return -1;
	}

	tmp = strdup(list);
	file = strtok(tmp, " ,|");
	while(file != NULL)
	{
		if(index == MAX_EXTER_LIB_NUMBER)
		{
			logger(PRIORITY_ERROR, "too many exter libraries.");
			rq_exter_destroy();
			free(tmp);
			return -1;
		}

		snprintf(libfile, FILE_PATH_SIZE, "%s/lib/%s.so", queue_root, file);		
		if((ext[index].library = dlopen(libfile, RTLD_LAZY)) == NULL)
			goto error_load;

		snprintf(function, 64, "%s_initialize", file);
		if(NULL == (initialize = dlsym(ext[index].library, function)))
			goto error_load;

		if(-1 == (re = (*initialize)())) {
			logger(PRIORITY_ERROR, "initialize code(%d), in library %s.so", re, file);
			goto error_load;
		}

		snprintf(function, 64, "%s_handler", file);
		if(NULL == (ext[index].handler = dlsym(ext[index].library, function)))
			goto error_load;

		snprintf(ext[index].extra, FILE_PATH_SIZE, "%s", file);
		file = strtok(NULL, " ,|");
		index++;
	}
	free(tmp);
	return 1;

error_load:
	logger(PRIORITY_ERROR, "%s at %d in '%s'.", dlerror(), __LINE__, __FILE__);
	free(tmp);
	return -1;
}

void rq_exter_destroy()
{
	char function[64];
	int i;
	rq_fini destroy;
	for(i = 0; i < MAX_EXTER_LIB_NUMBER; i++)
	{
		if(ext[i].library == NULL)
			continue;

		snprintf(function, 64, "%s_destroy", ext[i].extra);
		if((destroy = dlsym(ext[i].library, function)) != NULL)
			(*destroy)();

		dlclose(ext[i].library);
		ext[i].library = NULL;
		ext[i].handler = NULL;
	}
}

